package com.micro.magupe.jdbc.db;

import java.io.PrintWriter;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.LinkedList;
import java.util.logging.Logger;

import javax.sql.DataSource;

/**
 * https://blog.csdn.net/hauchun/article/details/80390395
 * https://ak478288.iteye.com/blog/1130515
 * https://wely.iteye.com/blog/2275725
 * https://blog.csdn.net/hbzyaxiu520/article/details/6130733
 * https://www.douban.com/note/663264294/
 */
public class MicroDataSource implements DataSource{

    static final String JDBC_DRIVER = "com.mysql.jdbc.Driver";  
    static final String DB_URL = "jdbc:mysql://127.0.0.1:3306/micro_magupe_log_master_dev?useUnicode=true&characterEncoding=utf8&useSSL=false";
    static final String USER = "root";
    static final String PASS = "880204";
    
    protected LinkedList<Connection> pools = new LinkedList<Connection>(); 
    
    private int initialSize;
    private int maxActive;
    private int maxIdle;
    private int minIdle;
    
    static {
    	try {
			Class.forName(JDBC_DRIVER);
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
    }
    
    public MicroDataSource(int initialSize) {
		int i = 0;
		try {
			for (i = 0; i < initialSize; i++) {
				Connection connection = getProxyConnection(DriverManager.getConnection(DB_URL, USER, PASS));
				pools.add(connection);
			}
		} catch (SQLException e) {
			e.printStackTrace();
		} 
    }
    
    public int getInitialSize() {
		return initialSize;
	}
	public void setInitialSize(int initialSize) {
		this.initialSize = initialSize;
	}
	public int getMaxActive() {
		return maxActive;
	}
	public void setMaxActive(int maxActive) {
		this.maxActive = maxActive;
	}
	public int getMaxIdle() {
		return maxIdle;
	}
	public void setMaxIdle(int maxIdle) {
		this.maxIdle = maxIdle;
	}
	public int getMinIdle() {
		return minIdle;
	}
	public void setMinIdle(int minIdle) {
		this.minIdle = minIdle;
	}

	// 检索此 DataSource对象的日志 writer。
	@Override
	public PrintWriter getLogWriter() throws SQLException {
		return null;
	}

	// 将此DataSource对象的日志 writer设置为给定的java.io.PrintWriter对象。
	@Override
	public void setLogWriter(PrintWriter out) throws SQLException {
		
	}

	// 设置数据源尝试连接到某一数据库时将等待的最长时间，以秒为单位。
	@Override
	public void setLoginTimeout(int seconds) throws SQLException {
		
	}

	// 获取此数据源尝试连接到某一数据库时可以等待的最长时间，以秒为单位。
	@Override
	public int getLoginTimeout() throws SQLException {
		return 0;
	}

	@Override
	public Logger getParentLogger() throws SQLFeatureNotSupportedException {
		return null;
	}

	// 返回一个对象，该对象实现给定接口，以允许访问非标准方法或代理未公开的标准方法。
	@Override
	public <T> T unwrap(Class<T> iface) throws SQLException {
		throw new SQLException("MagupeDataSource is not a wrapper.");
	}

	// 如果调用此方法的对象实现接口参数，或者是实现接口参数的对象的直接或间接包装器，则返回 true。
	@Override
	public boolean isWrapperFor(Class<?> iface) throws SQLException {
		return false;
	}

	public Connection getConnection() throws SQLException { 
		synchronized (pools) {
			Connection connection = null;
			if(pools.size()==0){
				try {
					pools.wait();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				return getConnection();
			}
			
	        if(pools.size() > 0){  
	        	// 从pools中获取第一个Connection，并且将其从pools中删除
	        	connection = pools.removeFirst();  
	        } else {
	        	connection = makeConnection();
	        }
	        
	        return connection;
		}
    } 

	@Override
	public Connection getConnection(String username, String password) throws SQLException {
		throw new UnsupportedOperationException("Not supported by MagupeDataSource");
	}

	private Connection makeConnection() throws SQLException { 
		Connection connection = getProxyConnection(DriverManager.getConnection(DB_URL, USER, PASS));
		pools.add(connection);
        return connection; 
	} 
	
	private Connection getProxyConnection(final Connection connection) {
		Object connectionProxyObject = Proxy.newProxyInstance(MicroDataSource.class.getClassLoader(), new Class[] {Connection.class}, new InvocationHandler() {
			@Override
			public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
				Object resultObject = null;
				if (method.getName().equals("close")) {
					synchronized (pools) {
						// 因为该Connection已经从pools中删除了，所以可以再次放进去
						pools.add((Connection) proxy);
					}
					resultObject = null;
				} else {
					resultObject = method.invoke(connection, args);
				}

				return resultObject;
			}
		});
		
		return (Connection) connectionProxyObject;
	}
}
